﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AudioDjStudio;
using AxSLIDERLib;

namespace IntelliTouchMix
{
    public enum PlayerDeck { 
        DECKA_PLAYER,
        DECKB_PLAYER,
        PLAYLIST_PLAYER,
        SAMPLE1_PLAYER,
        SAMPLE2_PLAYER
    }

    public enum SongSource { 
        FILE,
        WEB,
        CD
    }

    public partial class FormMain
    {
        public Int16 SAMPLE1 = 0;
        public Int16 SAMPLE2 = 1;
        SongInfo[] playerInfo;
        public String[] samplePath;
        public Int32 defaultMode = 500;
        public Int16[] players;
        public enumEffects selectedEffect = enumEffects.CHORUS;

        private delegate void SetControlPropertyThreadSafeDelegate(Control control, string propertyName, object propertyValue);

        public enumErrorCodes changeMasterMonitor(Int16 player, bool master)
        {
            if (player == Util.DECK_A)
            {
                Util.deckA.switchMonitor.Value = master;
            }
            else if (player == Util.DECK_B)
            {
                Util.deckB.switchMonitor.Value = master;
            }
            return switchMasterMonitor(player,master);
        }

        public enumErrorCodes switchMasterMonitor(Int16 player, bool master)
        {
            enumErrorCodes nError = enumErrorCodes.NOERROR;
            if (master)
            {
                nError = audioDjStudio1.StreamOutputDeviceSet(player, (Int16)Util.master_device);
                nError = audioDjStudio1.StreamOutputSpeakerSet(player, (enumSpeakers)Util.master_speakers);
            }
            else
            {
                nError = audioDjStudio1.StreamOutputDeviceSet(player, (Int16)Util.monitor_device);
                nError = audioDjStudio1.StreamOutputSpeakerSet(player, (enumSpeakers)Util.monitor_speakers);
            }
            return nError;
        }

        public void changeEffect(enumEffects effect,bool on)
        {
            this.form1.setEffect(effect,on);
        }

        public void initUserCode()
        {
            int i;
            for (playerInfo = new SongInfo[i = 4]; i > 0; playerInfo[--i] = new SongInfo()) ;
            samplePath = new String[2];
            players = new Int16[]{DECKA_PLAYER,DECKB_PLAYER,PLAYLIST_PLAYER,SAMPLE1_PLAYER,SAMPLE2_PLAYER};
            samplePath[SAMPLE1] = "c:\\a.wav";
            samplePath[SAMPLE2] = "c:\\b.wav";
        }

        public void changeConfig()
        {
            configureAudio(Util.master_device, Util.monitor_device, Util.master_speakers, Util.monitor_speakers);
        }

        private void configureAudio(Int16 masterDevice,Int16 monitorDevice,Int16 masterSpeakers,Int16 monitorSpeakers)
        {
            if (masterDevice == monitorDevice &&
                masterSpeakers == monitorSpeakers)
            {
                /*
                DialogResult result;
                result = MessageBox.Show("Warning: This demo gives its best when dealing with two different output " +
                                    "device/speaker pair; you have chosen the same output device/speaker pair for Master and Monitor " +
                                    "so you want be able to use the Monitor output with your headphones: do you want to select another " +
                                    "sound card for your Monitor output?", "Same output device", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
                if (result == DialogResult.Yes)
                    // don't close the form
                    return;

                frmMain.BtnOutputMaster1.Enabled = false;
                frmMain.BtnOutputMaster2.Enabled = false;
                frmMain.BtnOutputMonitor1.Enabled = false;
                frmMain.BtnOutputMonitor2.Enabled = false;
                MessageBox.Show("Monitor switch disabled", "Same output device", MessageBoxButtons.OK, MessageBoxIcon.Information);
                 * */
            }
            else
            {
                /*
                frmMain.BtnOutputMaster1.Enabled = true;
                frmMain.BtnOutputMaster2.Enabled = true;
                frmMain.BtnOutputMonitor1.Enabled = true;
                frmMain.BtnOutputMonitor2.Enabled = true;
                 * */
            }

            m_selOutputMaster = masterDevice;
            m_selOutputMonitor = monitorDevice;

            m_selSpeakersMaster = masterSpeakers;
            m_selSpeakersMonitor = monitorSpeakers;

            audioDjStudio1.BufferLength = 500;
            m_nScrollingWaveformViewInMs = 10000;
            if (m_nScrollingWaveformViewInMs < 500)
                m_nScrollingWaveformViewInMs = 500;

            //Close();
        }

        public void loadUserCode()
        {
            this.audioDjStudio1.BeatsAvailableOffline += new AudioDjStudio.AudioDjStudio.BeatsAvailableOfflineEventHandler(audioDjStudio1_BeatsAvailableOffline);
            this.audioDjStudio1.BeatDetectedRealTime += new AudioDjStudio.AudioDjStudio.BeatDetectedRealTimeEventHandler(audioDjStudio1_BeatDetectedRealTime);
            loadSample(SAMPLE1_PLAYER, samplePath[SAMPLE1]);
            loadSample(SAMPLE2_PLAYER, samplePath[SAMPLE2]);
            //loadUrl("http://www.construye-t.org.mx/song.mp3");
        }

        public enumErrorCodes playGesture(bool master)
        {
            enumErrorCodes exit = enumErrorCodes.NOERROR;
            bool mode = false;
            if (Util.selectedPanel == Util.DECK_A)
            {
                mode = Util.deckA.switchMonitor.Value;
            }
            else if (Util.selectedPanel == Util.DECK_B)
            {
                mode = Util.deckB.switchMonitor.Value;
            }
            List<Int16> listPlaying = getPlayerStatus(enumPlayerStatus.SOUND_PLAYING);
            if (Util.selectedPanel == Util.DECK_A || Util.selectedPanel == Util.DECK_B)
            {
                changeMasterMonitor((Int16)Util.selectedPanel, master);
                if (listPlaying.Count > 0)
                {
                    int oppositePanel = Util.selectedPanel == Util.DECK_A ? Util.DECK_B : Util.DECK_A;
                    if (listPlaying.Contains((Int16)Util.selectedPanel))
                    {
                        if (listPlaying.Contains((Int16)oppositePanel))
                        {
                        }
                        else
                        {
                            //if (mode == master)
                            //{
                                exit = audioDjStudio1.PauseSound((Int16)Util.selectedPanel);
                            //}
                        }
                    }
                    else
                    {
                        playFadeSongs();
                    }
                }
                else 
                {
                    exit = audioDjStudio1.PlaySound((Int16)Util.selectedPanel);
                }
            }
            else if (Util.selectedPanel == Util.MONITOR_PLAYER)
            {
                if (listPlaying.Count > 0)
                {
                    if(listPlaying.Contains((Int16)Util.selectedPanel))
                    {
                        exit = audioDjStudio1.StopSound((Int16)Util.selectedPanel);
                    }
                }
                if (Util.playList.PlayList1.SelectedIndex != null)
                {
                    exit = audioDjStudio1.PlayListExecAt(PLAYLIST_PLAYER, (Int16)Util.playList.PlayList1.SelectedIndex);
                }
                else if (Util.playList.PlayList1.Items.Count > 0)
                {
                    Util.playList.PlayList1.SelectedIndex = 0;
                    exit = audioDjStudio1.PlayListExecAt(PLAYLIST_PLAYER, 0);
                }
            }
            else
            {
                actionPlay();
            }
            return exit;
        }


        public enumErrorCodes loadSample(Int16 nPlayerIndex,String sample)
        {
            enumErrorCodes error = enumErrorCodes.NOERROR;
            if(!String.IsNullOrEmpty(sample))
            {
                error = audioDjStudio1.LoadSound(nPlayerIndex, sample);
                if(error==enumErrorCodes.NOERROR)
                {
                    samplePath[nPlayerIndex==SAMPLE1_PLAYER ? SAMPLE1 : SAMPLE2] = sample;
                }
                else
                {
                    MessageBox.Show(error.ToString());
                }
            }
            return error;
        }

        public Int16 addToPlayList(String path, SongSource songSource)
        {
            return addToPlayList(new String[] { path }, songSource);
        }

        public Int16 addToPlayList(String[] path,SongSource songSource)
        {
            Int16 exit = -1;
            ListBox.SelectedIndexCollection indexes = Util.playList.PlayList1.SelectedIndices;
            short nInsertPosition;
            if (indexes.Count > 0)
                nInsertPosition = exit = (short)(indexes[indexes.Count - 1] + 1);
            else
                nInsertPosition = exit = (short)Util.playList.PlayList1.Items.Count;
            enumErrorCodes nResult = enumErrorCodes.NOERROR;
            if (songSource == SongSource.FILE)
            {
                nResult = audioDjStudio1.PlayListAddItem(PLAYLIST_PLAYER, path[0], nInsertPosition);
            }
            else if (songSource == SongSource.WEB)
            {
                nResult = audioDjStudio1.PlayListAddItem(PLAYLIST_PLAYER, path[0], nInsertPosition);
            }
            else if (songSource == SongSource.CD)
            {
                nResult = audioDjStudio1.PlayListAddItem(PLAYLIST_PLAYER, path[2], nInsertPosition);
            }
            if (nResult != enumErrorCodes.NOERROR)
            {
                DisplayError(nResult);
                return -1;
            }
            PlayListAddItem(PLAYLIST_PLAYER);
            Util.playList.PlayList1.SelectedIndex = nInsertPosition;
            return exit;
        }

        public List<String> getAudioDevices()
        {
            List<String> exit = new List<String>();
            int nOutputDevices = audioDjStudio1.GetOutputDevicesCount();
            for (short i = 0; i < nOutputDevices; i++)
            {
                exit.Add(audioDjStudio1.GetOutputDeviceDesc(i));
            }
            return exit;
        }

        public List<String> getAudioSpeakers(int selectedAudioDevice)
        {
            List<String> exit = new List<String>();
            int nSpeakers = audioDjStudio1.GetOutputSpeakersCount((Int16)selectedAudioDevice);

            // enable stereo speakers
            if (nSpeakers >= 8)
            {
                // 7.1 device
                exit.Add("Front");
                exit.Add("Rear");
                exit.Add("Center");
                exit.Add("Rear Center");
            }
            else if (nSpeakers >= 6)
            {
                // 5.1 device
                exit.Add("Front");
                exit.Add("Rear");
                exit.Add("Center");
            }
            else if (nSpeakers >= 4)
            {
                // Quadraphonic device
                exit.Add("Front");
                exit.Add("Rear");
            }
            else
                // stereo device
                exit.Add("Front");
            return exit;
        }

        

        public void loadUrl(String url)
        {
            addToPlayList(url,SongSource.WEB);
        }

        public void actionPlay()
        {
            List<Int16> players = getPlayerStatus(enumPlayerStatus.SOUND_PLAYING);
            enumErrorCodes error = enumErrorCodes.NOERROR;
            if (players.Count > 0)
            {
                if(players.Contains(DECKA_PLAYER) && players.Contains(DECKB_PLAYER)){}
                else if(players.Contains(DECKA_PLAYER) || players.Contains(DECKB_PLAYER))
                {
                    playFadeSongs();
                }
            }
            else 
            {
                players = getPlayerStatus(enumPlayerStatus.SOUND_PAUSED);
                if (players.Count > 0)
                {
                    audioDjStudio1.PlaySound(players[0]);
                }
                else if ((error = audioDjStudio1.PlaySound(DECKA_PLAYER)) == enumErrorCodes.NOERROR){
                }
                else if ((error = audioDjStudio1.PlaySound(DECKB_PLAYER)) == enumErrorCodes.NOERROR){
                }
                else if (Util.playList.PlayList1.SelectedIndex != null)
                {
                    error = audioDjStudio1.PlayListExecAt(PLAYLIST_PLAYER, (Int16)Util.playList.PlayList1.SelectedIndex);
                }
                else if (Util.playList.PlayList1.Items.Count > 0)
                {
                    Util.playList.PlayList1.SelectedIndex = 0;
                    error = audioDjStudio1.PlayListExecAt(PLAYLIST_PLAYER, 0);
                }
                else
                {
                    //MessageBox.Show(error.ToString());
                }
            }
        }

        public void actionStop()
        {
            List<Int16> players = getPlayerStatus(enumPlayerStatus.SOUND_PLAYING);
            for (int i = 0; i < players.Count; i++)
            {
                audioDjStudio1.StopSound(players[i]);
            }
        }

        public void actionPause()
        {
            List<Int16> players = getPlayerStatus(enumPlayerStatus.SOUND_PLAYING);
            for (int i = 0; i < players.Count; i++)
            {
                audioDjStudio1.PauseSound(players[i]);
            }
        }

        public void actionNext()
        {
            if (Util.playList.PlayList1.Items.Count>0)
            {
                if (Util.playList.PlayList1.SelectedIndex != null)
                {
                    if ((Util.playList.PlayList1.SelectedIndex + 1) < Util.playList.PlayList1.Items.Count)
                    {
                        Util.playList.PlayList1.SelectedIndex++;
                    }
                }
                else 
                {
                    Util.playList.PlayList1.SelectedIndex = 0;
                }
            }
        }

        public void actionLast()
        {
            if (Util.playList.PlayList1.Items.Count > 0)
            {
                if (Util.playList.PlayList1.SelectedIndex != null)
                {
                    if (Util.playList.PlayList1.SelectedIndex > 0)
                    {
                        Util.playList.PlayList1.SelectedIndex--;
                    }
                }
                else
                {
                    Util.playList.PlayList1.SelectedIndex = 0;
                }
            }
        }

        public enumErrorCodes playSample(Int16 nPlayerIndex)
        {
            enumErrorCodes error = enumErrorCodes.NOERROR;
            String sample = samplePath[nPlayerIndex==SAMPLE1_PLAYER ? SAMPLE1 : SAMPLE2];
            if(!String.IsNullOrEmpty(sample))
            {
                if (audioDjStudio1.GetPlayerStatus(nPlayerIndex) == enumPlayerStatus.SOUND_PLAYING)
                {
                    audioDjStudio1.StopSound(nPlayerIndex);
                }
                error = audioDjStudio1.PlaySound(nPlayerIndex);
            }
            return error;
        }

        public void ManageTouchDownOnScroll(Int16 nPlayerIndex, TouchMouseEventArgs e)
        {

            enumPlayerStatus status = audioDjStudio1.GetPlayerStatus(nPlayerIndex);
            if (status == enumPlayerStatus.SOUND_PLAYING)
            {
                audioDjStudio1.PauseSound(nPlayerIndex);
                m_bMustResumeOnMouseUp[nPlayerIndex] = true;
            }
            else
                m_bMustResumeOnMouseUp[nPlayerIndex] = false;

            enumScaleUnits nScaleUnits = audioDjStudio1.ScaleUnits;
            audioDjStudio1.ScaleUnits = enumScaleUnits.SCALE_MILLISECONDS;

            PictureBox pictureBoxWaveformScroll;
            PictureBox pictureBoxWaveformFull;
            if (nPlayerIndex == DECKA_PLAYER)
            {
                pictureBoxWaveformScroll = pictureBoxWaveformScroll1;
                pictureBoxWaveformFull = pictureBoxWaveformFull1;
            }
            else
            {
                pictureBoxWaveformScroll = pictureBoxWaveformScroll2;
                pictureBoxWaveformFull = pictureBoxWaveformFull2;
            }

            // determine the position of the click from pixels to ms
            Int32 nViewWidthInPixels = pictureBoxWaveformScroll.Width;
            Int32 nViewWidthInMs = m_nWaveformScrollEndPosInMs[nPlayerIndex] - m_nWaveformScrollStartPosInMs[nPlayerIndex];

            float fPixelDurationInMs = (float)nViewWidthInMs / (float)nViewWidthInPixels;

            m_nPixelPositionOnWaveform[nPlayerIndex] = (int)e.distance;

            int sign = e.senseType == SenseType.POSITIVE ? 1 : -1;

            int soundDuration = audioDjStudio1.GetSoundDuration(nPlayerIndex);
            Int32 advance = (Int32)(Util.snapValue[Util.snapStatus] * playerInfo[nPlayerIndex].mode * sign);
            Console.WriteLine(advance);
            Int32 nPositionToSeekInMs = m_nWaveformScrollStartPosInMs[nPlayerIndex] + advance + 5000;
            Console.WriteLine(nPositionToSeekInMs);
            if (nPositionToSeekInMs < 0)
                nPositionToSeekInMs = 0;
            if (nPositionToSeekInMs <= 0 && e.senseType == SenseType.POSITIVE)
                nPositionToSeekInMs = advance;
            if (nPositionToSeekInMs > soundDuration)
                nPositionToSeekInMs = soundDuration;

            // move the current playback position
            audioDjStudio1.SeekSound(nPlayerIndex, (UInt32)nPositionToSeekInMs);

            // update scrolling waveform
            DrawWaveformScrolling(nPlayerIndex, pictureBoxWaveformScroll);

            // update full waveform
            float fPixelDurationOnFullInMs = soundDuration / pictureBoxWaveformFull.Width;
            m_nCurrentPosInPixels[nPlayerIndex] = (Int32)((float)audioDjStudio1.GetPlaybackTime(nPlayerIndex) / fPixelDurationOnFullInMs);
            DrawWaveformFull(nPlayerIndex, pictureBoxWaveformFull, m_nCurrentPosInPixels[nPlayerIndex]);

            audioDjStudio1.ScaleUnits = nScaleUnits;
        }

        void audioDjStudio1_BeatDetectedRealTime(object sender, BeatDetectedRealTimeEventArgs e)
        {
            Console.WriteLine("BEAT");
        }

        private void ManageTouchOnFull(Int16 nPlayerIndex, TouchMouseEventArgs e)
        {
            enumScaleUnits nScaleUnits = audioDjStudio1.ScaleUnits;
            audioDjStudio1.ScaleUnits = enumScaleUnits.SCALE_MILLISECONDS;

            PictureBox pictureBoxWaveformScroll;
            PictureBox pictureBoxWaveformFull;
            if (nPlayerIndex == DECKA_PLAYER)
            {
                pictureBoxWaveformScroll = pictureBoxWaveformScroll1;
                pictureBoxWaveformFull = pictureBoxWaveformFull1;
            }
            else
            {
                pictureBoxWaveformScroll = pictureBoxWaveformScroll2;
                pictureBoxWaveformFull = pictureBoxWaveformFull2;
            }

            // determine the position of the click from pixels to ms
            Int32 nViewWidthInPixels = pictureBoxWaveformScroll.Width;
            Int32 nViewWidthInMs = audioDjStudio1.GetSoundDuration(nPlayerIndex);

            float fPixelDurationInMs = (float)nViewWidthInMs / (float)nViewWidthInPixels;

            Int32 nPixelPositionOnWaveform = (Int32)e.distance;

            Int32 nPositionToSeekInMs = (Int32)((float)nPixelPositionOnWaveform * fPixelDurationInMs);

            // move the current playback position
            audioDjStudio1.SeekSound(nPlayerIndex, (UInt32)nPositionToSeekInMs);

            // update scrolling waveform
            DrawWaveformScrolling(nPlayerIndex, pictureBoxWaveformScroll);

            // update full waveform
            float fPixelDurationOnFullInMs = audioDjStudio1.GetSoundDuration(nPlayerIndex) / pictureBoxWaveformFull.Width;
            m_nCurrentPosInPixels[nPlayerIndex] = (Int32)((float)audioDjStudio1.GetPlaybackTime(nPlayerIndex) / fPixelDurationOnFullInMs);
            DrawWaveformFull(nPlayerIndex, pictureBoxWaveformFull, m_nCurrentPosInPixels[nPlayerIndex]);

            audioDjStudio1.ScaleUnits = nScaleUnits;
        }

        

        void audioDjStudio1_BeatsAvailableOffline(object sender, BeatsAvailableOfflineEventArgs e)
        {
            audioDjStudio1.BeatsDetectEnableRT(e.nPlayerIndex, true);

            Dictionary<Int32, Int32> listDif = new Dictionary<Int32, Int32>();
            for (int i = 0; i < e.nBeatsDetected; i++)
            {
                Int32 nBeatPos = audioDjStudio1.BeatsDetectGetBeatPos(0, i);
                listDif.Add(nBeatPos,i>0?audioDjStudio1.BeatsDetectGetBeatPos(0, i) - audioDjStudio1.BeatsDetectGetBeatPos(0, i - 1):0);
                //Console.WriteLine(audioDjStudio1.GetFormattedTime(nBeatPos, false, true));
            }
            var mode = listDif.Values.GroupBy(n => n).
                       OrderByDescending(g => g.Count()).
                       Select(g => g.Key).FirstOrDefault();

            audioDjStudio1.SetForwardRewindGranularity(e.nPlayerIndex, mode);

            mode = mode == 0 ? defaultMode : mode;
            playerInfo[e.nPlayerIndex].mode = mode;
            playerInfo[e.nPlayerIndex].bpmMode = 60000 / mode;
            playerInfo[e.nPlayerIndex].beatsInfo = listDif;
        }

        public List<String> getCDDrives()
        {
            List<String> exit = new List<String>();
            Int16 nCdDrivesCount = audioDjStudio1.GetCdDriveCount();
            if (nCdDrivesCount > 0)
            {
                for (Int16 i = 0; i < nCdDrivesCount; i++)
                    exit.Add(audioDjStudio1.GetCdDriveDesc(i));
            }
            return exit;
        }

        public void loadCD(Int16 selectedDrive)
        {
            if(selectedDrive>=0)
            {
                audioDjStudio1.EnableCdMonitor(true);
                audioDjStudio1.CddbRequestServersList();
                Int16 nCdTracksCount = audioDjStudio1.GetCdNumTracks(selectedDrive);

                // list CD tracks
                for (Int16 i = 1; i <= nCdTracksCount; i++)
                {
                    string strIndex = i.ToString();
                    Int32 nDurationInMilliSec = audioDjStudio1.GetCdTrackDuration(selectedDrive, i) * 1000;
                    string strDuration = audioDjStudio1.GetFormattedTime(nDurationInMilliSec, true, false);
                    string strTitle = audioDjStudio1.GetCdTrackPathname(selectedDrive, i);
                    string[] listStrings = new string[] { strIndex, strDuration, strTitle };
                    ListViewItem item = new ListViewItem(listStrings, -1);
                    addToPlayList(listStrings, SongSource.CD);
                }
            }
        }

        public void analizeSong(short player)
        {
            playerInfo[player] = new SongInfo();
            float fBandwidth = 10;
            float fCutoffFreq = 90;
            float fReleaseTime = 20;
            audioDjStudio1.BeatsDetectSetParams(player, fBandwidth, fCutoffFreq, fReleaseTime);
            int m_nSoundDuration = audioDjStudio1.GetSoundDuration(player);
            audioDjStudio1.BeatsDetectSetRange(player, 0, m_nSoundDuration);
            audioDjStudio1.BeatsDetectRequest(player);
        }

        public List<Int16> getPlayerStatus(enumPlayerStatus status)
        {
            List<Int16> exit = new List<Int16>();
            for (Int16 i = 0; i < players.Length; i++)
            {
                if (audioDjStudio1.GetPlayerStatus(i) == status)
                    exit.Add(i);
            }
            return exit;
        }

        public List<Int16> getPlayersPaused()
        {
            List<Int16> exit = new List<Int16>();
            for (Int16 i = 0; i < players.Length; i++)
            {
                if (audioDjStudio1.GetPlayerStatus(i) == enumPlayerStatus.SOUND_PAUSED)
                    exit.Add(i);
            }
            return exit;
        }

        public AxGSlider getPitchTrackbar()
        {
            List<Int16> player = getPlayerStatus(enumPlayerStatus.SOUND_PLAYING);
            if(player.Count > 0)
            {
                if (player.Contains(DECKA_PLAYER))
                {
                    return GSlider_Tempo1;
                }
                else if (player.Contains(DECKB_PLAYER))
                {
                    return GSlider_Tempo2;
                }
            }
            return null;
        }

        public void playFadeSongs()
        {
            BtnStartFading_Click(new object(), EventArgs.Empty);
        }

        public void loadDeckA()
        {
            BtnLoadDeckA_Click(new object(), EventArgs.Empty);
        }
        public void loadDeckB()
        {
            BtnLoadDeckB_Click(new object(), EventArgs.Empty);
        }


        public static void SetControlPropertyThreadSafe(Control control, string propertyName, object propertyValue)
        {
            if (control.InvokeRequired)
            {
                control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue });
            }
            else
            {
                control.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue });
            }
        }




    }
}
